use core::fmt::Write;
use core::ptr::{self, addr_of, addr_of_mut};

use log::{LevelFilter, Log};
use mlibs::buf::DMABuffer;
use mlibs::cmsis_rtos2::event_flags::EventFlags;
use mlibs::cmsis_rtos2::message_queue::{mq_size, MessageQueue};
use mlibs::cmsis_rtos2::OS_WAIT_FOREVER;
use mlibs::periph::gpio::PA;
use mlibs::periph::{config_dma, config_gpio};
use mlibs::periph::{dma::DMA2, interrupt, rcc::RCC, uart::USART1, Interrupt, NVIC};

static mut DMA_RX_BUF: DMABuffer<u8, 128> = DMABuffer::default();
static mut DMA_TX_BUF: DMABuffer<u8, 128> = DMABuffer::default();
static mut EVENT_FLAGS: EventFlags = EventFlags::default();
const RECV_MQ_SIZE: usize = mq_size::<&[u8]>(5);
static mut RECV_MQ: MessageQueue<&[u8], 5, RECV_MQ_SIZE> = MessageQueue::default();
static mut RECV_INDEX: u16 = 0;

const FLAG_DMA_TC: u32 = 0x01;
const FLAG_LOG: u32 = 0x02;

#[config_gpio(TX_PA9_AF7_PP_FLOAT, RX_PA10_AF7_PP_FLOAT)]
struct UARTPin;

#[config_dma(DMA2_STREAM7_CH4_SIZE8)]
struct DMATx;

#[config_dma(DMA2_STREAM5_CH4_SIZE8)]
struct DMARx;

struct Uart;

pub fn init() {
    UARTPin.init();
    RCC.apb2enr.modify(|_, w| w.usart1en().enabled());
    DMATx.init_rcc();
    DMARx.init_rcc();

    USART1.cr1.reset();
    USART1.cr2.reset();
    USART1.cr3.write(|w| w.dmar().enabled().dmat().enabled());
    USART1.brr.write(|w| unsafe { w.bits(84000000 / 115200) });
    USART1.cr1.write(|w| {
        w.idleie()
            .enabled()
            .ue()
            .enabled()
            .te()
            .enabled()
            .re()
            .enabled()
    });

    unsafe {
        EVENT_FLAGS.new("uart");
        _ = EVENT_FLAGS.set_flags(FLAG_DMA_TC | FLAG_LOG);
        RECV_MQ.new("uart");

        DMARx.read_with_circ(USART1.dr.as_ptr() as u32, DMA_RX_BUF.as_mut());

        NVIC::unmask(Interrupt::USART1);
        NVIC::unmask(Interrupt::DMA2_STREAM7);
    }
    _ = log::set_logger(&Uart);
    _ = log::set_max_level(LevelFilter::Debug);
}

pub fn write(buf: &[u8]) {
    unsafe {
        _ = EVENT_FLAGS.wait_any_flags(FLAG_DMA_TC, OS_WAIT_FOREVER);
        write_by_dma(buf);
    }
}

pub fn recv() -> &'static[u8] {
    unsafe { RECV_MQ.poll(OS_WAIT_FOREVER).unwrap() }
}

unsafe fn write_by_dma(buf: &[u8]) {
    let tx_buf = DMA_TX_BUF.as_mut();
    ptr::copy_nonoverlapping(buf.as_ptr(), tx_buf.as_mut_ptr(), buf.len());
    DMATx.write_with_it(USART1.dr.as_ptr() as u32, &tx_buf[..buf.len()]);
}

impl Write for Uart {
    fn write_str(&mut self, s: &str) -> core::fmt::Result {
        write(s.as_bytes());
        Ok(())
    }
}

impl Log for Uart {
    fn enabled(&self, _metadata: &log::Metadata) -> bool {
        true
    }
    fn flush(&self) {}
    fn log(&self, record: &log::Record) {
        unsafe {
            _ = EVENT_FLAGS.wait_any_flags(FLAG_LOG, OS_WAIT_FOREVER);
            _ = writeln!(
                Uart,
                "[{}] {}:{} => {}",
                record.level(),
                record.file_static().unwrap_or(""),
                record.line().unwrap_or(0),
                record.args()
            );
            _ = EVENT_FLAGS.set_flags(FLAG_LOG);
        }
    }
}

unsafe fn recv_idle() {
    let recv_index = ptr::read_volatile(addr_of!(RECV_INDEX));
    let index = DMA_RX_BUF.count() -  DMARx.ndtr();
    if index == recv_index {
        return;
    }
    let recv_buf = DMA_RX_BUF.as_ref();
    if index > recv_index {
        let buf = &recv_buf[recv_index as usize..index as usize];
        _ = RECV_MQ.post(&buf);
    } else {
        let buf = &recv_buf[recv_index as usize..];
        _ = RECV_MQ.post(&buf);
        if index != 0 {
            let buf = &recv_buf[..index as usize];
            _ = RECV_MQ.post(&buf);
        }
    }
    ptr::write_volatile(addr_of_mut!(RECV_INDEX), index);
}

#[interrupt]
unsafe fn USART1() {
    if USART1.sr.read().idle().bit_is_set() {
        USART1.dr.read();
        recv_idle();
    }
}

#[interrupt]
unsafe fn DMA2_STREAM7() {
    if DMATx.is_complete() {
        DMATx.clear_flags();
        _ = EVENT_FLAGS.set_flags(FLAG_DMA_TC);
    }
}
